private package fhq.hcmute.qrcode;

import fhq.hcmute.qrcode.common.BitMatrix;
import fhq.hcmute.qrcode.common.DataMode;
import fhq.hcmute.qrcode.common.ErrorCorrectionLevel;
import fhq.hcmute.qrcode.common.MaskPattern;
import fhq.hcmute.qrcode.common.Version;
import fhq.hcmute.qrcode.encoder.BitArray;
import fhq.hcmute.qrcode.reedsolomon.Monomial;
import fhq.hcmute.qrcode.reedsolomon.Polynomial;
import fhq.hcmute.qrcode.util.QRCodeException;

/**
 * @author: Hien Nguyen Le
 * @Email: nglehien@gmail.com
 */
public class QRCode {
	private ErrorCorrectionLevel level;
	private MaskPattern maskPattern;
	private BitMatrix matrix;
	private Version version;
	private DataMode dataMode;
	private String contents;

	public BitMatrix getDataMatrix() {
		return matrix;
	}

	public void setBitMatrix(BitMatrix bitMatrix) {
		this.matrix = bitMatrix;
	}

	public Version getVersion() {
		return version;
	}

	public QRCode(ErrorCorrectionLevel level, MaskPattern maskPattern,
			Version version, DataMode dataMode, String contents) {
		this.level = level;
		this.maskPattern = maskPattern;
		this.version = version;
		this.matrix = new BitMatrix(version);
		this.setDataMode(dataMode);
		this.contents = contents;
	}

	public void appendFormatInformation() throws QRCodeException {
		BitArray specialBitString = new BitArray("101010000010010");
		Polynomial gx = new Polynomial(new Monomial(1, 10), new Monomial(1, 8),
				new Monomial(1, 5), new Monomial(1, 4), new Monomial(1, 2),
				new Monomial(1, 1), new Monomial(1, 0));
		BitArray bitArray = new BitArray();
		bitArray.append(level.getValue() + maskPattern.getValue());
		Polynomial fx = new Polynomial();
		for (int i = bitArray.getSize() - 1; i >= 0; i--) {
			boolean c = bitArray.getBitAt(i);
			if (c) {
				fx.add(new Monomial(1, bitArray.getSize() - 1 - i));
			}
		}
		fx = fx.multiply(new Monomial(1, 15 - 5));
		if (fx.getSize() > 0) {
			fx.devide(gx);
			for (int i : fx.getRemainder().getArrayCoefficient(10)) {
				bitArray.append(1, i);
			}
		}
		bitArray.xor(specialBitString);
		int[] formatInformation = bitArray.getBinaryArray();
		this.matrix.setFormatInformation(formatInformation);
	}

	public void appendBitDatas(BitArray bitArray) {
		this.matrix.appendDatas(bitArray, maskPattern);
	}

	public void appendVersionInfomation() throws QRCodeException {
		BitArray versionInformation = version.encode();
		if (versionInformation.getSize() != 0) {
			this.matrix.setVersionInformation(
					versionInformation.getBinaryArray(), maskPattern);
		}

	}

	public void draw() {
		int[][] matrix = this.matrix.getRawValues();
		for (int i = 0; i < matrix.length; i++) {
			for (int j = 0; j < matrix.length; j++) {
				if (matrix[i][j] == BitMatrix.TRUE) {
					System.out.print("1 ");
				} else if (matrix[i][j] == BitMatrix.FALSE) {
					System.out.print("0 ");
				} else if (matrix[i][j] == BitMatrix.EMPTY) {
					System.out.print(" ");
				}
			}
			System.out.println();
		}
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("\r\nVersion: " + version.getVersionNumber());
		builder.append("\r\nData Mode: " + dataMode.name());
		builder.append("\r\nContents: " + contents);
		builder.append("\r\nError Code Correction: " + level.name());
		builder.append("\r\nMask Pattern: " + maskPattern.name());
		return builder.toString();
	}

	public DataMode getDataMode() {
		return dataMode;
	}

	public void setDataMode(DataMode dataMode) {
		this.dataMode = dataMode;
	}

	public ErrorCorrectionLevel getLevel() {
		return level;
	}

	public MaskPattern getMaskPattern() {
		return maskPattern;
	}

	public String getContents() {
		return contents;
	}

}
